/*
 *	Copyright (C) 2008 CRIMERE
 *	Copyright (C) 2008 Jean-Marc Mercier
 *	
 *	This file is part of OTS (optimally transported schemes), an open-source library
 *	dedicated to scientific computing. http://code.google.com/p/optimally-transported-schemes/
 *
 *	CRIMERE makes no representations about the suitability of this
 *	software for any purpose. It is provided "as is" without express or
 *	implied warranty.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include <set>
#include <src/algorithm/EvolutionAlgorithm.h>
#include <src/data/ParticleSet.h>
#include <src/math/functor/2D_representation.h>
#include <src/miscellaneous/Key_Generator.h>


/*
	This class is a base class for all Cauchy Problems. It is just used to build the base classes.
	Devrait disparaitre au profit de la classe EvolutionAlgorithm !!
*/

#if !defined(_CauchyProblemTraits_)
#define _CauchyProblemTraits_

template <class Interface>
class Cauchy_Problem_list_type {
public :
	typedef typename Interface					interface_type;
	typedef typename interface_type::key_type	key_type;
	Cauchy_Problem_list_type(){};
	void add_list(smart_ptr<interface_type> from)
	{
		cauchy_list_[ from->get_name() ] = from;
		key_list_[ from->get_key() ]	 = from;
		graphical_interface_ = from;
	};
	smart_ptr<interface_type> Select(const std::string& _string) {
		graphical_interface_ = cauchy_list_[_string];
		return graphical_interface_;
	};
	smart_ptr<interface_type> operator[](key_type key) {
		graphical_interface_ = key_list_[key];
		return graphical_interface_;
	};
	smart_ptr<interface_type> operator()() {return graphical_interface_;};

	smart_ptr<interface_type> graphical_interface_;
	typedef std::map< std::string, smart_ptr<interface_type> > cauchy_list_type;
	typedef typename cauchy_list_type::iterator cauchy_list_iterator_type;
	cauchy_list_type cauchy_list_;
	std::map< key_type, smart_ptr<interface_type> > key_list_;
};

class Graphical_Interface : public Key_generator<>
{
public :
	typedef Cauchy_Problem_list_type<Graphical_Interface> Cauchy_list_type;
	Graphical_Interface()
	{
	};
	static Cauchy_list_type & instance() {return interfaces_list_type::instance();};
	void Register() {instance().add_list(Create_Instance());};


	typedef particle<OS_double>												p_array_1;
	typedef particles_list<p_array_1>										p_array_2;
	typedef std::set< std::pair<OS_int, OS_int> > graph_type;
	virtual void initial_conditions( OS_size nb ) = 0;
	virtual p_array_2 get_representation() = 0;
	virtual void set_dim(OS_size dim) = 0;
	virtual OS_size get_dim() = 0;
	virtual OS_size get_number_of_grid_points() const = 0;
	virtual OS_size get_iterations() = 0;
	virtual OS_double operator++() = 0;
	virtual std::string get_name() = 0;
	virtual OS_double get_current_time() const = 0;
	virtual void insert_time(OS_double time) = 0;
	virtual void reset_time( ) = 0;
	virtual smart_ptr<Graphical_Interface> Create_Instance() {
		OS_DYNAMIC_CHECK(false,"to be overloaded in derived class");
	};
	virtual const graph_type& get_graph() const = 0;
	virtual void set_viscosity(OS_double viscosity)
	{
		OS_DYNAMIC_CHECK(false,"to be overloaded in derived class");
	};

private :
	typedef Singleton<Cauchy_list_type > interfaces_list_type;
};

//static Cauchy_Problem_list_type<Graphical_Interface> Cauchy_Problem_list_;


		template <class TimeAlgorithm, class Interface >
		class CauchyProblem_traits : public Interface
		{
		public :
			typedef typename Interface												interface_type;
			typedef typename interface_type::graph_type								graph_type;

			typedef typename TimeAlgorithm											algorithm_type;
			typedef typename EvolutionAlgorithm< algorithm_type >					evolution_algorithm_type;
			typedef typename evolution_algorithm_type::solution_type				solution_type;
			typedef typename algorithm_type::result_type							result_type;
			typedef typename algorithm_type::result_type							particles_list_type;

			typedef typename evolution_algorithm_type::solution_type				solution_type;
			typedef typename evolution_algorithm_type::time_type					time_type;
			typedef typename algorithm_type::time_operator_type						time_operator_type;
			typedef typename time_operator_type::state_variable_type				operator_state_variable_type;

			virtual smart_ptr<interface_type> Create_Instance() = 0;
			virtual ~CauchyProblem_traits(){};

			virtual std::string get_name() {return algorithm_type::get_name()
													+time_operator_type::get_name()
													+time_operator_type::Boundary_type::get_name()
													+ result_type::get_name()
													+". "
													+ time_operator_type::Family_Operator::get_name();
			};


			CauchyProblem_traits()
			{
				insert_time(0);
				insert_time(1e+10);
			};

			void set_initial_conditions(const result_type & from) 
			{
				evolution_algorithm_.set_solution(from);
			};

			time_type get_current_time() const {return evolution_algorithm_.get_current_time();};
			virtual void reset_time() {
				evolution_algorithm_.reset_time();
			};
			virtual OS_size get_iterations() {return evolution_algorithm_.get_iterations();};

			void insert_time(time_type time) {evolution_algorithm_.insert_time(time);};


			time_type operator++() { //increment time
				++evolution_algorithm_;
				return get_current_time();
			};
			const EvolutionAlgorithm< algorithm_type > & get_evolution_algorithm() const {return evolution_algorithm_;};
			const algorithm_type & get_algorithm() const {return evolution_algorithm_.get_algorithm();};
			const time_operator_type& get_operator() const {return get_algorithm().get_operator();};
			const solution_type& get_solution() const {return evolution_algorithm_.get_solution();};

			virtual const graph_type& get_graph() const
			{
				return get_operator().get_graph();
			};
			virtual typename interface_type::p_array_2 get_representation() {
				return representation_2<result_type,result_type::Dim,time_operator_type::Family_Operator>()(get_solution());
			};
			virtual OS_size get_number_of_grid_points() const {
				return get_solution().size();
			};;



		protected :
			EvolutionAlgorithm< algorithm_type > & get_evolution_algorithm() {return evolution_algorithm_;};
			algorithm_type & get_algorithm() {return evolution_algorithm_.get_algorithm();};
			time_operator_type& get_operator() {return get_algorithm().get_operator();};
			evolution_algorithm_type	evolution_algorithm_;
		};

#endif